﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
using System.Threading;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;

namespace OrangePrj.Forms
{
    public partial class FormModelView : Form
    {
        string OrangeProjectFilterString = "Orange Project files (*.org, *.ops)|*.org; *.ops|All files (*.*)|*.*";
        private List<Panel> mng_panels = new List<Panel>();
        TreeNode selectedNode;
        TreeNode currentNode;
        Test test = new Test();
        double frameFrom;
        double frameTo;
        double frameStep;

        public FormModelView(Project project)
        {
            InitializeComponent();
            this.test.project = (Project)project.Clone();
            
            if (test.project.Name!=null)
                textBoxPrjName.Text = test.project.Name;

            currentNode = new TreeNode() { Text = test.project.Name, Tag = test.Clone() };
            treeViewVariants.Nodes.Add((TreeNode)currentNode.Clone());
            mng_panels.AddRange(new List<Panel> { panelManageProbability, panelFunctions, panelAvailableResources });
            ManageParameterSelector.SelectedIndex = 0;
            ManageParameterSelector_SelectedValueChanged(this, null);
        }

        private void ToModel(object sender, EventArgs e)
        {
            try
            {
                if (test.project.worklist.Count == 0)
                {
                    throw new NullReferenceException("Проект не загружен.");
                }
                OptionsReader();
                Modelling model = new Modelling(test.project.worklist);
                progressBar.Maximum = test.iterations;
                model.Onchanged += new Modelling.ProgressEventhandler(Progress_Onchanged);
                model.Iterations = this.test.iterations;
                test.statistics = new StatArray();
                test.statistics = model.Run();
                SubmitNode();
                FillCharts();
                FillInfo();
            }
            catch (NullReferenceException ex)
            {
                MessageBox.Show(ex.Message, "Внимание", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
        }

        void Progress_Onchanged(int value)
        {
            progressBar.Value = value;
        }

        private void FillInfo()
        {
            if (test.statistics != null)
            {
                textBoxPrjProb.Text = (test.statistics.CritWorks.YData[test.statistics.CritWorks.YData.Count - 1] * 100 / test.iterations).ToString() + " %";
                textBoxMPPrjTime.Text = test.statistics.PrjTime.XData[test.statistics.PrjTime.GetMaxYid()].ToString();
                textBoxMPRes.Text = test.statistics.Res.XData[test.statistics.Res.GetMaxYid()].ToString();
                textBoxPrjName.Text = test.project.Name;
                IterationsCounter.Value = test.iterations;
            }
        }

        private void SubmitNode()
        {
            try
            {
                currentNode.Tag = test.Clone();
                if (selectedNode == null)
                    treeViewVariants.Nodes[0].Nodes.Add((TreeNode)currentNode.Clone());
                else
                    selectedNode.Nodes.Add((TreeNode)currentNode.Clone());
            }
            catch (ArgumentException ex)
            {
                MessageBox.Show("Испытание с такими параметрами уже существует.", "Внимание", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
        }

        private void OptionsReader()
        {
            test.iterations = (int)IterationsCounter.Value;
        }
        
        private void FillCharts()
        {
            if (test.statistics != null)
            {
                double[] prjtime = new double[test.statistics.PrjTime.YData.Count];
                for (int i = 0; i < prjtime.Length; i++)
                {
                    prjtime[i] = test.statistics.PrjTime.YData[i] * 100 / test.iterations;
                }
                double[] res = new double[test.statistics.Res.YData.Count];
                for (int i = 0; i < res.Length; i++)
                {
                    res[i] = test.statistics.Res.YData[i] * 100 / test.iterations;
                }
                chartPrjTime.Series["Default"].Points.DataBindXY(test.statistics.PrjTime.XData, prjtime);
                chartRes.Series["Default"].Points.DataBindXY(test.statistics.Res.XData, res);
                List<double> crit = new List<double>();
                foreach (double d in test.statistics.CritWorks.YData)
                {
                    crit.Add(d * 100 / test.iterations);
                }
                chartDoneWorks.Series["Default"].Points.DataBindXY(test.statistics.CritWorks.XData, crit);
                chartCritWorks.Series["Default"].Points.DataBindXY(test.statistics.CritWorks.XData, crit);
            }
        }

        private void Export_Click(object sender, EventArgs e)
        {
            SaveFileDialog dialog = new SaveFileDialog();
            dialog.AddExtension = true;
            dialog.DefaultExt = ".ops";
            if (dialog.ShowDialog() == DialogResult.OK)
            {
                BinaryFormatter binFormat = new BinaryFormatter();
                using (Stream fStream = new FileStream(dialog.FileName, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    binFormat.Serialize(fStream, treeViewVariants.Nodes[0]);
                }
            }
        }

        private void Import_Click(object sender, EventArgs e)
        {
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.Filter = OrangeProjectFilterString;

            if (dialog.ShowDialog() == DialogResult.OK)
            {
                treeViewVariants.Nodes.Clear();
                FileInfo info = new FileInfo(dialog.FileName);
                BinaryFormatter binFormat = new BinaryFormatter();
                switch (info.Extension)
                {
                    case ".org":
                        using (Stream fStream = File.OpenRead(dialog.FileName))
                        {
                            test.project = (Project)binFormat.Deserialize(fStream);
                        }
                        currentNode = new TreeNode() { Text = test.project.Name, Tag=test.Clone()};
                        treeViewVariants.Nodes.Add((TreeNode)currentNode.Clone());
                        MessageBox.Show("Проект загружен, можно приступать к моделированию.", "Загрузка завершена", MessageBoxButtons.OK, MessageBoxIcon.Information);
                        break;

                    case ".ops":
                        using (Stream fStream = File.OpenRead(dialog.FileName))
                        {
                            treeViewVariants.Nodes.Add((TreeNode)binFormat.Deserialize(fStream));
                        }
                        MessageBox.Show("Серия испытаний загружена, можно приступать к моделированию.", "Загрузка завершена", MessageBoxButtons.OK, MessageBoxIcon.Information);
                        break;
                    default:
                        MessageBox.Show("Такой формат не поддерживается", "Неверный формат", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                        break;
                } 
            }
        }

        private void ManageParameterSelector_SelectedValueChanged(object sender, EventArgs e)
        {
            if (test.project != null)
            {
                switch (ManageParameterSelector.SelectedIndex)
                {
                    case (int)ManageParameters.Probability:
                        PropabilityPanelInit();
                        break;
                    case (int)ManageParameters.Functions:
                        FunctionsPanelInit();
                        break;
                    case (int)ManageParameters.AvailableResource:
                        ResourcePanelInit();
                        break;
                    default:
                        MessageBox.Show("Неподдерживаемый управляемый параметр");
                        break;
                }
                ManagePanelChooser(ManageParameterSelector.SelectedIndex);
            }
            else
                MessageBox.Show("Сначала загрузите или выбирете проект", "Проект не выбран", MessageBoxButtons.OK, MessageBoxIcon.Warning);
        }
 
        private void ManagePanelChooser(int panelIndex)
        {
            for (int i = 0; i < mng_panels.Count; i++)
            {
                if (i != panelIndex)
                    mng_panels[i].Visible = false;
                else
                    mng_panels[panelIndex].Visible = true;
            }
        }
        
        private void PropabilityPanelInit()
        {
            comboBoxWorkSelector.Items.Clear();
            foreach (Work w in test.project.worklist)
            {
                if (w.Type != WorkType.Простая)
                    comboBoxWorkSelector.Items.Add(w);
            }
            if (comboBoxWorkSelector.Items.Count != 0)
                comboBoxWorkSelector.SelectedItem = comboBoxWorkSelector.Items[0];
        }

        private void ResourcePanelInit()
        {
            AbsoluteResourceAmount.Value = (decimal)test.absoluteRes.Amount;
            RelativeResourceAmount.Value = (decimal)test.relativeRes.Amount;
        }

        private void FunctionsPanelInit()
        {
            comboBoxWorkSelectorFunc.Items.Clear();
            foreach (Work w in test.project.worklist)
            {
                if (w.Type != WorkType.Простая)
                    comboBoxWorkSelectorFunc.Items.Add(w);
            }
            if (comboBoxWorkSelectorFunc.Items.Count != 0)
                comboBoxWorkSelectorFunc.SelectedIndex = 0;
            FunctionSelector.Items.Clear();
            FunctionSelector.Items.AddRange(new object[]{Functions.Линейная, Functions.Экспоненциальная, Functions.Показательная});
        }

        private void trackBarProb_Changed(object sender, EventArgs e)
        {
            WorkProbInput.Text = ((double)trackBarProb.Value / trackBarProb.Maximum).ToString("f2");
            Work w = (Work)comboBoxWorkSelector.SelectedItem;
            if (trackBarProb.Value != (int)(w.Probability * 100))
                buttonApplyProb.Enabled = true;
            else
                buttonApplyProb.Enabled = false;
        }
        
        private void comboBoxWorkSelector_SelectedIndexChanged(object sender, EventArgs e)
        {
            Work w = (Work)(comboBoxWorkSelector.SelectedItem);
            trackBarProb.Value = (int)(w.Probability * 100);
        }
        
        private void buttonApplyProb_Click(object sender, EventArgs e)
        {
            Work w = (Work)comboBoxWorkSelector.SelectedItem;
            double prob_old = w.Probability;
            w.Probability = (double)trackBarProb.Value/100;
            test.project.worklist.UpdateWork(w);
            buttonApplyProb.Enabled = false;
            if (prob_old != w.Probability)
            {
                currentNode.Text = "Вероятность " + w.Name + " " + prob_old + " -> " + w.Probability;
            }
            ToModel(sender, e);
        }

        private void buttonResourceApply_Click(object sender, EventArgs e)
        {
            currentNode = new TreeNode();
            double abs_old = test.absoluteRes.Amount;
            double rel_old = test.relativeRes.Amount;
            test.absoluteRes.Amount = (double)AbsoluteResourceAmount.Value;
            test.relativeRes.Amount = (double)RelativeResourceAmount.Value;
            buttonResourceApply.Enabled = false;

            if (abs_old != test.absoluteRes.Amount)
            {
                currentNode.Text = "Ресурсы: Абс " + abs_old + " -> " + test.absoluteRes.Amount;
            }
            if (rel_old != test.relativeRes.Amount)
            {
                currentNode.Text = "Ресурсы: Отн " + rel_old + " -> " + test.relativeRes.Amount;
            }
            ToModel(sender, e);
        }

        private void ResourceAmount_ValueChanged(object sender, EventArgs e)
        {
            if ((double)AbsoluteResourceAmount.Value != test.absoluteRes.Amount)
            {
                buttonResourceApply.Enabled = true;
            }
            else if ((double)RelativeResourceAmount.Value != test.relativeRes.Amount)
            {
                buttonResourceApply.Enabled = true;
            }
            else
                buttonResourceApply.Enabled = false;
        }

        private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {

        }

        private void treeViewVariants_AfterSelect(object sender, TreeViewEventArgs e)
        {
            selectedNode = treeViewVariants.SelectedNode;
            if (selectedNode != null)
            {
                Test t = (Test)selectedNode.Tag;
                test = (Test)t.Clone();
                FillCharts();
                FillInfo();
                UpdateAllPanels();
            }
        }
        private void UpdateAllPanels()
        {
            PropabilityPanelInit();
            FunctionsPanelInit();
            ResourcePanelInit();
        }

        private void interval_Scroll(object sender, EventArgs e)
        {
            switch (ManageParameterSelector.SelectedIndex)
            {
                case 0:
                    if (frameFrom==0)
                        IntervalFromButton.Text = ((double)interval.Value / interval.Maximum).ToString("f2");
                    else if (frameTo==0)
                        IntervalToButton.Text = ((double)interval.Value / interval.Maximum).ToString("f2");
                    break;
                case 2:
                    if (frameFrom==0)
                        IntervalFromButton.Text = interval.Value.ToString();
                    else if (frameTo==0)
                        IntervalToButton.Text = interval.Value.ToString();
                    break;
                default:
                    break;
            }
        }

        private void IntervalFromButton_Click(object sender, EventArgs e)
        {
            if (frameFrom == 0)
                double.TryParse(IntervalFromButton.Text, out frameFrom);
            else
                frameFrom = 0;
            CheckIntervalParams();
        }

        private void IntervalToButton_Click(object sender, EventArgs e)
        {
            if (frameTo == 0)
                double.TryParse(IntervalToButton.Text, out frameTo);
            else
                frameTo = 0;
            CheckIntervalParams();
        }

        private void CheckIntervalParams()
        {
            buttonIntervalApply.Enabled = false;
            double.TryParse(intervalStep.Text, out frameStep);
            if (frameStep < frameTo - frameFrom)
                if (frameTo != 0)
                    if (frameStep != 0)
                        buttonIntervalApply.Enabled = true;
        }

        private void intervalStep_TextChanged(object sender, EventArgs e)
        {
            CheckIntervalParams();
        }

        private void buttonIntervalApply_Click(object sender, EventArgs e)
        {
            switch (ManageParameterSelector.SelectedIndex)
            {
                case 0:
                    Work w = (Work)comboBoxWorkSelector.SelectedItem;
                    double prob_old = w.Probability;
                    double curr_value = frameFrom;
                    do
                    {
                        w.Probability = curr_value;
                        test.project.worklist.UpdateWork(w);
                        currentNode.Text = "Вероятность " + w.Name + " " + prob_old + " -> " + w.Probability;
                        ToModel(sender, e);
                        curr_value += frameStep;
                    }
                    while (curr_value <=frameTo);
                    break;
                case 2:
                    
                    break;
                default:
                    break;
            }
        }

    }
}
